package com.vidmt.api.modules.sys.service;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.thinkgem.jeesite.common.service.CrudService;
import com.vidmt.api.common.anno.WebApiUse;
import com.vidmt.api.common.exceptions.UserExistsException;
import com.vidmt.api.common.exceptions.UserNotExistsException;
import com.vidmt.api.common.utils.CommUtil;
import com.vidmt.api.common.utils.IDUtil;
import com.vidmt.api.modules.sys.Acc;
import com.vidmt.api.modules.sys.Enums.AccType;
import com.vidmt.api.modules.sys.Enums.UserStatus;
import com.vidmt.api.modules.sys.auth.AuthFactory;
import com.vidmt.api.modules.sys.dao.LocationDao;
import com.vidmt.api.modules.sys.dao.TraceDao;
import com.vidmt.api.modules.sys.dao.UserDao;
import com.vidmt.api.modules.sys.entity.User;
import com.vidmt.api.modules.sys.entity.sub.RoleLvl;
import com.vidmt.api.modules.sys.providers.ResProvider;
import com.vidmt.api.modules.sys.utils.ResUtil;

@Service
@Transactional(readOnly = false)
@CacheConfig(cacheNames = "userCache")
public class UserService extends CrudService<UserDao, User> {
	private static final Logger log = LoggerFactory.getLogger(UserService.class);
	@Autowired
	private LocationDao locationDao;
	@Autowired
	private TraceDao traceDao;

	@Autowired
	private ImService imService;
	@Autowired
	private ResProvider resProvider;

	@Cacheable(key = "'uid_'+#id")
	public User load(Long id) {
		log.warn("load user from db:" + id);
		User user = dao.load(id);
		// if (user != null) {
		// this.checkLvlttl(user);
		// }
		return user;
	}

	@Transactional(readOnly = true)
	public String getPwd(Long uid) {
		return dao.getPassword(uid);
	}

	public void changePwd(String password, Long uid) throws UserNotExistsException {
		int rows = dao.updateColumn(uid, "pwd", AuthFactory.fmtPwd(uid, password));
		if (rows == 0) {
			throw new UserNotExistsException(new Acc(AccType.uid, uid.toString()));
		}
		imService.changePassword(uid, password);
	}

	@CacheEvict(key = "'uid_'+#uid")
	public void updateUserExpired(Long uid) {
		log.warn("update user from db:" + uid);
		// UserCache.remove(uid);
		dao.updateUserExpired(uid);
	}

	@CacheEvict(key = "'uid_'+#uid")
	public void updateUserPayed(Long uid, RoleLvl lvl) {
		log.warn("update user from db:" + uid);
		// UserCache.remove(uid);
		dao.updateUserPayed(uid, lvl.getLvltype(), lvl.getDuring());
	}

	@Transactional(readOnly = true)
	public Long getUserCount() {
		return dao.getUserCount();
	}

	public User register(Acc acc, String plainpwd, String nick) throws UserExistsException {
		if (CommUtil.isEmpty(plainpwd)) {
			throw new IllegalArgumentException("password cant be empty");
		}
		User user = acc.toUser();
		user.setStatus(UserStatus.NORMAL);
		user.setSex(User.SEX_F);
		user.setNick(nick);
		user.setBirth(new java.sql.Date(System.currentTimeMillis() - 20L * 365 * 24 * 60 * 60 * 1000));
		try {
			IDUtil idutil = IDUtil.get(User.class);
			synchronized (idutil) {
				if (idutil.current() < 0) {
					long max = dao.maxUid();
					idutil.reset(max);
				}
				long uid = idutil.current() + 1;
				user.setId(uid);
				dao.insert(user);
				dao.updateColumn(uid, "pwd", AuthFactory.fmtPwd(uid, plainpwd));
				User[] uarr = { user };
				String[] pwdarr = { plainpwd };
				idutil.increment();
				imService.addUser(uarr, pwdarr);
			}

			return user;
		} catch (DuplicateKeyException e) {
			throw new UserExistsException("用户已经存在" + acc);
		}
	}

	@Transactional(readOnly = true)
	public User findByAcc(Acc acc) {
		User user = null;
		if (acc.type() == AccType.uid) {
			// 我们没有用aspectj，因此为了使用proxy，为了使缓存生效，此处不能使用uid
			// user = this.load(Long.valueOf(acc.value()));
			throw new IllegalArgumentException("please use load method if acc.type==uid");
		} else {
			user = dao.findByColumn(acc.type().name(), acc.value());
		}
		return user;
	}

	@Transactional(readOnly = true)
	public List<User> findByUids(Long[] uids) {
		List<User> ulists = dao.findByUids(Arrays.asList(uids));
		return ulists;
	}

	@CacheEvict(key = "'uid_'+#user.id")
	public int update(User user) {
		imService.updateUser(user);
		return dao.update(user);
	}

	@CacheEvict(key = "'uid_'+#uid")
	public void deleteByUid(Long uid) {
		log.warn("remove user from db:" + uid);
		IDUtil idutil = IDUtil.get(User.class);
		synchronized (idutil) {
			dao.deleteByColumn("id", uid);
			locationDao.deleteByUid(uid);
			traceDao.deleteByUid(uid);
			imService.deleteUser(new Long[] { uid });
			if (idutil.current() == uid) {
				idutil.decrement();
			}
			String userdir = ResUtil.buildUserDir(uid);
			try {
				resProvider.deleteFile(userdir);
			} catch (IOException e) {
				log.error("删除用户文件目录失败", e);
			}
		}
	}

	@WebApiUse
	public Map<Long, Boolean> isOnline(Long... uids) {
		try {
			return imService.isOnline(uids);
		} catch (UserNotExistsException e) {
			return Collections.emptyMap();
		}
	}

	/**
	 * 如果是通过api需要返回给客户端的，都需要调用此函数，计算会员剩余时间。 因此此函数只在controller中调用
	 */
	public void checkLvlttl(User user) {
		if (user.getLvl() != null) {
			Date lvlend = user.getLvlEnd();
			long ttl = (lvlend.getTime() - System.currentTimeMillis()) / 1000;
			if (ttl > 0) {
				user.setLvlttl(ttl);
			} else {
				this.updateUserExpired(user.getId());
				user.setStatus(UserStatus.NORMAL);
				user.setLvl(null);
				user.setLvlttl(null);
			}
		}
	}
}
